package com.hudson.core.util;

/**
 * 雪花算法实现
 */
public class SnowflakeIdGenerator {

    /**
     * 默认一个节点
     */
    public static final SnowflakeIdGenerator DEFAULT = new SnowflakeIdGenerator(1);

    // 每一部分占用的位数
    private final long nodeIdBits = 10L;  // 节点ID占用的位数
    private final long sequenceBits = 12L; // 序列号占用的位数

    // 每一部分的最大值
    private final long maxSequence = ~(-1L << sequenceBits); // 序列号最大值：4095

    private final long nodeIdShift = sequenceBits; // 节点ID左移位数
    private final long timestampLeftShift = sequenceBits + nodeIdBits; // 时间戳左移位数

    private long lastTimestamp = -1L; // 上一次生成ID的时间戳
    private long sequence = 0L; // 序列号
    private final long nodeId; // 节点ID

    private final long nodeIdMax = ~(-1L << nodeIdBits); // 节点ID最大值：1023

    /**
     * 构造函数
     * @param nodeId 节点ID，范围 0~1023
     */
    public SnowflakeIdGenerator(long nodeId) {
        if (nodeId > nodeIdMax || nodeId < 0) {
            throw new IllegalArgumentException(String.format("节点ID超出范围：必须在0~%d之间", nodeIdMax));
        }
        this.nodeId = nodeId;
    }

    /**
     * 生成唯一ID
     * @return 64位long型ID
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        // 如果当前时间小于上一次生成ID的时间戳，说明系统时钟回退了
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时钟回退");
        }

        // 如果是同一毫秒生成的，则进行序列号处理
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & maxSequence;
            // 序列号溢出，等待下一毫秒
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 新的一毫秒，序列号从0开始
            sequence = 0;
        }

        lastTimestamp = timestamp;

        // 拼接64位ID
        return (timestamp << timestampLeftShift)
                | (nodeId << nodeIdShift)
                | sequence;
    }

    /**
     * 等待直到下一毫秒
     * @param lastTimestamp 上一次生成ID的时间戳
     * @return 当前时间戳
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public static void main(String[] args) {
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1); // 节点ID为1
        for (int i = 0; i < 10; i++) {
            long id = idGenerator.nextId();
            System.out.println("生成的ID：" + id);
        }
    }
}
